﻿using System;
using System.Collections.Generic;
using System.Linq;
using StockholmsStad.Data.Context;
using StockholmsStad.Data.ContractFactory;
using StockholmsStad.Data.Entity;
using StockholmsStad.Data.Interfaces;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using StockholmsStad.Utilities.Enums;
using StockholmsStad.Utilities.Exceptions;
using Ticket = StockholmsStad.Runtime.Serialization.DataContract.Ticket;
using Booking = StockholmsStad.Runtime.Serialization.DataContract.Booking;
using ParVisProperties = StockholmsStad.Runtime.Serialization.DataContract.ParVisProperties;
using TimeBlock=StockholmsStad.Runtime.Serialization.DataContract.TimeBlock;
using StockholmsStad.Data.Properties;

namespace StockholmsStad.Data.DatabaseAgents
{
    internal class BookingAgent : DBAgentBase, IBookingAgent
    {
        #region IBookingAgent Members

        
        public TimeBlock GetTimeBlockByStartDateTime(IDataContext context, DateTime startDateTime)
        {
            Entity.TimeBlock timeBlock = GetTimeBlockEntityByStartDateTime(context, startDateTime);
            if (timeBlock == null)
                return null;
            BookingFactory factory = FactoryAgent.GetBookingFactory();
            TimeBlock contractObject = factory.CreateContractTimeBlock(timeBlock);
            return contractObject;
        }

        
        public void RemoveBooking(IDataContext context, int ticketId, string removedBy, string recipient, string message)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                dbCtx.RemoveBooking(ticketId, removedBy, recipient, message);
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        
        public int AddBooking(IDataContext context, int ticketId, Booking booking, int? ministerId)
        {
            BookingFactory factory = FactoryAgent.GetBookingFactory();
            try
            {
                Entity.Booking entityBooking = null;
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var ticket = dbCtx.Tickets.Where(t => t.TicketId == ticketId).FirstOrDefault();
                
                if (booking.CeremonyType == CeremonyType.Ordinary)
                {
                    if(booking.DateTime != null)
                    {
                        //Skapa nytt jämförelsedatum utan sekunder och milisekunder
                        var sdt = booking.DateTime.StartDateTime;
                        var compareDateTime = new DateTime(sdt.Year, sdt.Month, sdt.Day, sdt.Hour, sdt.Minute, 0);
                        var timeBlock =
                            dbCtx.TimeBlocks.Where(t => t.StartDateTime.Equals(compareDateTime)).FirstOrDefault();
                        
                        //Kolla om tidsblocket redan är bokat
                        CheckTimeBlockNotAlreadyBooked(dbCtx, timeBlock.TimeBlockId);

                        OrdinaryBooking ordinaryBookingEntity = factory.CreateOrdinaryBookingEntityObject(booking, timeBlock, ticket);
                        entityBooking = ordinaryBookingEntity.Booking;
                        dbCtx.Bookings.InsertOnSubmit(entityBooking);
                        dbCtx.OrdinaryBookings.InsertOnSubmit(ordinaryBookingEntity);
                    }

                    if(booking.PreferredDate1.HasValue)
                    {
                        var preferredDates = dbCtx.PreferredDates.
                            Where(d => d.TicketId == ticketId).FirstOrDefault() ?? new PreferredDate();

                        preferredDates.PreferredDate1 = booking.PreferredDate1.Value;
                        preferredDates.PreferredDate2 = booking.PreferredDate2;
                        preferredDates.TicketId = ticketId;
                        dbCtx.PreferredDates.InsertOnSubmit(preferredDates);
                    }
                    dbCtx.SubmitChanges();
                }
                else
                {
                    if(!ministerId.HasValue)
                    {
                        throw new StockholmStadSystemException(SystemErrorCode.IndividualBookingMinisterNotProvided);
                    }
                     
                    Minister minister = dbCtx.Ministers.Where(m => m.MinisterId == ministerId.Value).Single();

                    IndividualBooking individualBooking = factory.CreateIndividualBookingEntityObject(booking, minister, ticket);
                    entityBooking = individualBooking.Booking;
                    dbCtx.Bookings.InsertOnSubmit(entityBooking);
                    dbCtx.IndividualBookings.InsertOnSubmit(individualBooking);
                    dbCtx.SubmitChanges();
                }
                return entityBooking != null ? entityBooking.BookingId : -1;
            }
            catch(StockholmStadSystemException)
            {
                throw;                
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }


        public void UpdateBooking(IDataContext context, int ticketId, Booking booking, int? ministerId, string updatedByUser)
        {
            BookingFactory factory = FactoryAgent.GetBookingFactory();
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Entity.Booking updateBooking = dbCtx.Bookings.Where(b => b.TicketId == ticketId).FirstOrDefault();
                IndividualBooking individualBooking = updateBooking == null ? null :
                    dbCtx.IndividualBookings.Where(ib => ib.BookingId == updateBooking.BookingId).FirstOrDefault();
                OrdinaryBooking ordinaryBooking = updateBooking == null ? null :
                    dbCtx.OrdinaryBookings.Where(ib => ib.BookingId == updateBooking.BookingId).FirstOrDefault();

                bool removeOrdinaryBooking = false;
                int removeOrdinaryTimeBlockId = ordinaryBooking != null ? ordinaryBooking.TimeBlockId : 0;
                bool removeIndividualBooking = false;

                var preferredDates = dbCtx.PreferredDates.Where(d => d.TicketId == ticketId).FirstOrDefault();
                
                if (booking.CeremonyType == CeremonyType.Ordinary)
                {
                    Entity.TimeBlock timeBlock = null;
                    if(booking.DateTime != null)
                    {
                        //Skapa nytt jämförelsedatum utan sekunder och milisekunder
                        var sdt = booking.DateTime.StartDateTime;
                        var compareDateTime = new DateTime(sdt.Year, sdt.Month, sdt.Day, sdt.Hour, sdt.Minute, 0);
                        timeBlock = dbCtx.TimeBlocks.Where(t => t.StartDateTime.Equals(compareDateTime)).FirstOrDefault();

                        //Kolla om tidsblocket redan är bokat
                        CheckTimeBlockNotAlreadyBooked(dbCtx, timeBlock.TimeBlockId);
                    }

                    if(booking.PreferredDate1.HasValue)
                    {
                        if (preferredDates != null)
                        {
                            preferredDates.PreferredDate1 = booking.PreferredDate1.Value;
                            preferredDates.PreferredDate2 = booking.PreferredDate2;
                            preferredDates.TicketId = ticketId;
                        }
                        else
                        {
                            preferredDates = new PreferredDate
                                                 {
                                                     PreferredDate1 = booking.PreferredDate1.Value,
                                                     PreferredDate2 = booking.PreferredDate2,
                                                     TicketId = ticketId
                                                 };
                            dbCtx.PreferredDates.InsertOnSubmit(preferredDates);
                        }
                    }
                    else
                    {
                        // Önskade tid har raderats, ta bort posten
                        if (preferredDates != null)
                        {
                            dbCtx.PreferredDates.DeleteOnSubmit(preferredDates);
                        }
                    }

                    if (ordinaryBooking != null)
                    {
                        if (timeBlock != null)
                        {
                            //Ändra bokning
                            factory.UpdateEntityObject(booking, updateBooking, ordinaryBooking, timeBlock);
                            if(ordinaryBooking.TimeBlockId != timeBlock.TimeBlockId)
                                removeOrdinaryBooking = true;
                        }
                        else
                        {
                            //Ta bort bokning
                            dbCtx.OrdinaryBookings.DeleteOnSubmit(ordinaryBooking);
                            removeOrdinaryBooking = true;
                        }
                    }
                    else
                    {
                        // remove individual
                        if (individualBooking != null)
                        {
                            dbCtx.IndividualBookings.DeleteOnSubmit(individualBooking);
                            removeIndividualBooking = true;
                        }
                        if (booking.DateTime != null)
                        {
                            if (timeBlock != null)
                            {
                                if(updateBooking == null)
                                {
                                    //add booking
                                    updateBooking = new Entity.Booking {TicketId = ticketId};
                                    dbCtx.Bookings.InsertOnSubmit(updateBooking);
                                }
                                // add ordinary
                                ordinaryBooking = new OrdinaryBooking();
                                factory.UpdateEntityObject(booking, updateBooking, ordinaryBooking, timeBlock);
                                dbCtx.OrdinaryBookings.InsertOnSubmit(ordinaryBooking);
                            }
                            else
                            {
                                throw new StockholmStadSystemException(SystemErrorCode.TimeBlockNotExists);
                            }
                        }
                    }
                }
                if (booking.CeremonyType == CeremonyType.Individual)
                {
                    var minister = dbCtx.Ministers.Where(m => m.MinisterId == ministerId.Value).Single();
                    if (individualBooking != null)
                    {
                        factory.UpdateEntityObject(booking, updateBooking, individualBooking, minister);
                    }
                    else
                    {
                        // remove ordinary
                        if(ordinaryBooking != null)
                        {
                            dbCtx.OrdinaryBookings.DeleteOnSubmit(ordinaryBooking);
                            removeOrdinaryBooking = true;
                        }

                        // remove preferredDates
                        if (preferredDates != null)
                        {
                            dbCtx.PreferredDates.DeleteOnSubmit(preferredDates);
                        }

                        if (updateBooking == null)
                        {
                            //add booking
                            updateBooking = new Entity.Booking { TicketId = ticketId };
                            dbCtx.Bookings.InsertOnSubmit(updateBooking);
                        }
                        // add individual
                        individualBooking = new IndividualBooking();
                        factory.UpdateEntityObject(booking, updateBooking, individualBooking, minister);
                        dbCtx.IndividualBookings.InsertOnSubmit(individualBooking);
                    }
                }
                dbCtx.SubmitChanges();

                if(removeIndividualBooking)
                {
                    string ministerMail = "";
                    var minister = dbCtx.Ministers.Where(m => m.MinisterId == individualBooking.MinisterId).Single();
                    if (minister != null)
                    {
                        ministerMail = minister.Person.EmailAddress;
                    } 
                    if (string.IsNullOrEmpty(ministerMail))
                    {
                        //Skapa notis
                        dbCtx.InsertHistory(updatedByUser, Resources.CancelledIndividualBookingMinisterHasNoEmail, ticketId, false, true);
                        dbCtx.SubmitChanges();
                    }
                    else
                    {
                        //Skicka mail till förrättare
                        var ticketAgent = AgentFactory.GetTicketAgent();
                        var ticket = ticketAgent.GetTicket(context, ticketId);
                        var indBooking = new Booking { DateTime = new TimeBlock { StartDateTime = individualBooking.Date } };
                        SendRemoveIndividualBookingToMinisterMail(context, ministerMail, ticket, indBooking);
                    }
                }
                if (removeOrdinaryBooking && removeOrdinaryTimeBlockId > 0)
                {
                    var date = dbCtx.TimeBlocks.Where(t => t.TimeBlockId == removeOrdinaryTimeBlockId).First();
                    dbCtx.NotifyCouplesWithPreferredDate(date.StartDate);
                }
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        /// <summary>
        /// Hitta alla bokningsbara tidblock som inte redan är kopplade
        /// till en ordinarie bokning. Kontrollera även startdatum.
        /// Omvandla sedan dessa till kontraktsobjekt
        /// </summary>
        /// <param name="context">Det databascontext som operationen köra i</param>
        /// <param name="fromDate"></param>
        /// <returns></returns>
        
        public List<TimeBlock> GetFreeTimeBlockDateTimes(IDataContext context, DateTime fromDate)
        {
            BookingFactory factory = FactoryAgent.GetBookingFactory();
            //List<Entity.TimeBlock> list;
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                // Explicit ToList för att exekvera frågan
                var list = dbCtx.GetFreeTimeBlocks(fromDate);
                List<TimeBlock> timeBlocks = list.Select(t => factory.CreateContractTimeBlock(t)).ToList();
                return timeBlocks;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        /// <summary>
        /// Hitta alla bokningsbara tidblock som inte redan är kopplade
        /// till en ordinarie bokning mellan ett visst datumintervall.
        /// Omvandla sedan dessa till kontraktsobjekt
        /// </summary>
        /// <param name="context">Det databascontext som operationen köra i</param>
        /// <param name="fromDate"></param>
        /// <param name="toDate"></param>
        /// <returns></returns>

        public List<TimeBlock> GetFreeTimeBlockDateTimes(IDataContext context, DateTime fromDate, DateTime toDate)
        {
            BookingFactory factory = FactoryAgent.GetBookingFactory();
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                // Explicit ToList för att exekvera frågan
                var list = dbCtx.GetFreeTimeBlocks(fromDate);
                List<TimeBlock> timeBlocks = list.Where(t => t.StartDate.Value == toDate).Select(t => factory.CreateContractTimeBlock(t)).ToList();
                return timeBlocks;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        /// <summary>
        /// Hämtar alla bokade dagar, dvs det första tidblocket för en dag
        /// som inte har några lediga tider. Enbart dagar som ligger efter frånDatum returneras
        /// </summary>
        /// <param name="context">Det databascontext som operationen köra i</param>
        /// <returns></returns>
        
        public IEnumerable<TimeBlock> GetBookedTimeBlockDates(IDataContext context)
        {
            BookingFactory factory = FactoryAgent.GetBookingFactory();
            List<Entity.TimeBlock> list;
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                // Explicit ToList för att exekvera frågan
                // Datumparametern är obsolete i SP'n
                list = dbCtx.GetBookedTimeBlocks(DateTime.Now).ToList();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
            IEqualityComparer<Entity.TimeBlock> dateComparer = new MyDateComparer();
            IEnumerable<TimeBlock> timeBlocks = list.Distinct(dateComparer)
                .Select(t => factory.CreateContractTimeBlock(t));
            return timeBlocks;
        }

        
        public bool HasTicketBooking(IDataContext context, int ticketId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var booking = dbCtx.Bookings.Where(b => b.TicketId == ticketId).FirstOrDefault();
                return booking != null;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }            
        }

        /// <summary>
        /// Returnerar en bokning eller null om den inte finns
        /// </summary>
        /// <param name="context">Det databascontext som operationen köra i</param>
        /// <param name="ticketId"></param>
        /// <returns></returns>
        
        public Booking GetBooking(IDataContext context, int ticketId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Entity.Booking booking = dbCtx.Bookings.Where(b => b.TicketId == ticketId).FirstOrDefault();
                var contractBooking = new Booking();

                // Kolla om det finns önskade tider
                var preferredDates = dbCtx.PreferredDates.Where(d => d.TicketId == ticketId).FirstOrDefault();

                if(booking == null && preferredDates == null)
                {
                    //Ingen bokning hittas så en tom bokning returneras
                    return null;
                }

                if (preferredDates != null)
                {
                    contractBooking.PreferredDate1 = preferredDates.PreferredDate1;
                    contractBooking.PreferredDate2 = preferredDates.PreferredDate2;
                    //Dessa måste sättas annars går det inte att skicka upp objectet
                    contractBooking.BookingId = booking != null ? booking.BookingId : -1;
                    contractBooking.CeremonyType = CeremonyType.Ordinary;
                    contractBooking.DateTime = null;
                    contractBooking.MinisterCode = null;                
                }


                if (booking != null)
                {
                    IndividualBooking individualBooking =
                        dbCtx.IndividualBookings.Where(ib => ib.BookingId == booking.BookingId).FirstOrDefault();

                    OrdinaryBooking ordinaryBooking =
                        dbCtx.OrdinaryBookings.Where(ob => ob.BookingId == booking.BookingId).FirstOrDefault();

                    BookingFactory factory = FactoryAgent.GetBookingFactory();
                    contractBooking = factory.CreateContractObject(booking, contractBooking, ordinaryBooking, individualBooking);
                }

                return contractBooking;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        private static void CheckTimeBlockNotAlreadyBooked(StockholmsStadDataContext dbCtx, int timeBlockId)
        {
            var timeBlockTaken =
                dbCtx.OrdinaryBookings.Where(b => b.TimeBlockId == timeBlockId).FirstOrDefault();
            if (timeBlockTaken != null)
            {
                throw new StockholmStadSystemException(SystemErrorCode.BookingTimeBlockAlreadyBooked);
            }
        }

        ///<summary>
        /// Skickar iväg mail att en bokning har blivit gjord eller skapar notis om mailadress saknas.
        ///</summary>
        ///<param name="context"></param>
        ///<param name="ticket"></param>
        
        public void SendOrdinaryBookingMail(IDataContext context, Ticket ticket)
        {
            try
            {
                StockholmsStadDataContext dbCtx = ((DataContext)context).StockholmsStadDataContext;
                Booking booking = GetBooking(context, ticket.TicketId);
                ITicketAgent ticketAgent = AgentFactory.GetTicketAgent();

                String recipientEmail = ticket.Couple.PersonDetails.Email;

                // Behövs den här koden?
                if (booking == null ||
                    (booking.DateTime == null && !booking.PreferredDate1.HasValue && !booking.PreferredDate2.HasValue))
                {
                    if (recipientEmail != null)
                        dbCtx.SendRemoveBookingMail(recipientEmail);
                    else
                        ticketAgent.InsertNotice(context, ticket.TicketId,
                                                 string.Format(Resources.CancelledBookingMailMissing,
                                                               ticket.Couple.Person1.FirstNames,
                                                               ticket.Couple.Person1.LastName,
                                                               ticket.Couple.Person2.FirstNames,
                                                               ticket.Couple.Person2.LastName), "System");
                    
                    return;
                }
                
                PrefDates dates;
                if (booking.DateTime == null)
                {
                    dates = new PrefDates(booking);

                    if (recipientEmail != null)
                        dbCtx.SendPreferredOrdinaryBookingMail(recipientEmail, dates.PrefDate1, dates.PrefDate2);
                    else
                        ticketAgent.InsertNotice(context, ticket.TicketId,
                                                string.Format(Resources.PreferredDatesBookingMailMissting,
                                                                ticket.Couple.Person1.FirstNames,
                                                                ticket.Couple.Person1.LastName,
                                                                ticket.Couple.Person2.FirstNames,
                                                                ticket.Couple.Person2.LastName), "System");
                    return;
                }

                ParVisProperties properties = AgentFactory.GetPropertiesAgent().GetProperties(context);
                DateTime bookingDateTime = booking.DateTime.StartDateTime;
                DateTime earliestApplyDateForImpedimentDoc =
                bookingDateTime.AddMonths(-properties.ImpedimentDocValidMonths);

                if (booking.PreferredDate1 == null && booking.PreferredDate2 == null)
                {
                    if (recipientEmail != null)
                        dbCtx.SendOrdinaryBookingWithoutPreferredDateMail(recipientEmail, bookingDateTime,
                                                                          earliestApplyDateForImpedimentDoc);
                    //Bugzilla 153 - ingen notis ska skapas
                    //else
                    //    ticketAgent.InsertNotice(context, ticket.TicketId,
                    //                            string.Format(Resources.PreliminaryBookingMailMissing,
                    //                                            ticket.Couple.Person1.FirstNames,
                    //                                            ticket.Couple.Person1.LastName,
                    //                                            ticket.Couple.Person2.FirstNames,
                    //                                            ticket.Couple.Person2.LastName), "System");
                    return;
                }

                dates = new PrefDates(booking);

                if (recipientEmail != null)
                    dbCtx.SendOrdinaryBookingMail(recipientEmail, bookingDateTime, dates.PrefDate1,
                                                    dates.PrefDate2, earliestApplyDateForImpedimentDoc);
                //Bugzilla 153 - ingen notis ska skapas
                //else
                //    ticketAgent.InsertNotice(context, ticket.TicketId,
                //                                string.Format(Resources.PreliminaryBookingMailMissing,
                //                                                ticket.Couple.Person1.FirstNames,
                //                                                ticket.Couple.Person1.LastName,
                //                                                ticket.Couple.Person2.FirstNames,
                //                                                ticket.Couple.Person2.LastName), "System");
                
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        ///<summary>
        /// Skickar iväg mail att en stadshuset-bokning har blivit gjord.
        ///</summary>
        ///<param name="context"></param>
        ///<param name="recipientEmail"></param>
        ///<param name="booking"></param>
        
        public void SendPreferredOrdinaryBookingMail(IDataContext context, string recipientEmail, Booking booking)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                PrefDates dates;
                if (booking.DateTime == null)
                {
                    dates = new PrefDates(booking);
                    dbCtx.SendPreferredOrdinaryBookingMail(recipientEmail, dates.PrefDate1, dates.PrefDate2);
                }        
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        ///<summary>
        /// Skickar iväg mail att en stadshuset-bokning har blivit gjord.
        ///</summary>
        ///<param name="context"></param>
        ///<param name="recipientEmail"></param>
        ///<param name="booking"></param>
        
        public void SendOrdinaryBookingWithoutPreferredDateMail(IDataContext context, string recipientEmail, Booking booking)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                ParVisProperties properties = AgentFactory.GetPropertiesAgent().GetProperties(context);
                DateTime bookingDateTime = booking.DateTime.StartDateTime;
                DateTime earliestApplyDateForImpedimentDoc =
                    bookingDateTime.AddMonths(-properties.ImpedimentDocValidMonths);

                if (booking.PreferredDate1 == null && booking.PreferredDate2 == null)
                {
                    dbCtx.SendOrdinaryBookingWithoutPreferredDateMail(recipientEmail, bookingDateTime,
                                                                      earliestApplyDateForImpedimentDoc);
                }
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="recipient"></param>
        /// <param name="booking"></param>
        
        public void SendRemoveBookingMail(IDataContext context, string recipient, Booking booking)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                dbCtx.SendRemoveBookingMail(recipient);
            }
            catch(Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="recipient"></param>
        
        public void SendRemoveBookingImpedimentDocMail(IDataContext context, string recipient)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                dbCtx.SendRemoveBookingImpedimentDocMail(recipient);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="recipient"></param>
        /// <param name="ticket"></param>
        /// <param name="booking"></param>
        
        public void SendRemoveIndividualBookingToMinisterMail(IDataContext context, string recipient, Ticket ticket, Booking booking)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                dbCtx.SendRemoveIndividualBookingToMinisterMail(recipient, booking.DateTime.StartDateTime,
                                                                ticket.Couple.Person1.FirstNames,
                                                                ticket.Couple.Person1.LastName,
                                                                ticket.Couple.Person2.FirstNames,
                                                                ticket.Couple.Person2.LastName);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        /////<summary>
        ///// Skickar iväg mail att en bokning på annan plats än stadshuset har blivit gjord.
        /////</summary>
        /////<param name="context"></param>
        /////<param name="recipientEmail"></param>
        /////<param name="booking"></param>
        
        //public void SendIndividualBookingMail(IDataContext context, string recipientEmail, Booking booking)
        //{
        //    try
        //    {
        //        if (booking.DateTime == null)
        //        {
        //            throw new StockholmStadSystemException(SystemErrorCode.BookingDateMissing);
        //        }

        //        StockholmsStadDataContext dbCtx = context.GetInternalContext();

        //        ParVisProperties properties = AgentFactory.GetPropertiesAgent().GetProperties(context);
        //        DateTime bookingDateTime = booking.DateTime.StartDateTime;
        //        DateTime earliestApplyDateForImpedimentDoc =
        //            bookingDateTime.AddMonths(-properties.ImpedimentDocValidMonths);

        //        dbCtx.SendIndividualBookingMail(recipientEmail, bookingDateTime, earliestApplyDateForImpedimentDoc);
        //    }
        //    catch (Exception ex)
        //    {
        //        throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
        //    }
        //}

        ///<summary>
        /// Skickar iväg mail att en stadshuset-bokning har blivit gjord.
        ///</summary>
        ///<param name="context"></param>
        ///<param name="ticket"></param>
        
        public void SendIndividualBookingMail(IDataContext context, Ticket ticket)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Booking booking = GetBooking(context, ticket.TicketId);
                String recipientEmail = ticket.Couple.PersonDetails.Email;

                if (booking.DateTime == null)
                {
                    throw new StockholmStadSystemException(SystemErrorCode.BookingDateMissing);
                }

                ParVisProperties properties = AgentFactory.GetPropertiesAgent().GetProperties(context);
                DateTime bookingDateTime = booking.DateTime.StartDateTime;
                DateTime earliestApplyDateForImpedimentDoc =
                    bookingDateTime.AddMonths(-properties.ImpedimentDocValidMonths);

                if (recipientEmail != null)
                {
                    dbCtx.SendIndividualBookingMail(recipientEmail, bookingDateTime, earliestApplyDateForImpedimentDoc);
                }
                //else
                //{
                //    //Bugzilla 153 - ingen notis ska skapas
                //    ticketAgent.InsertNotice(context, ticket.TicketId,
                //                                string.Format(Resources.PreliminaryBookingMailMissing,
                //                                                ticket.Couple.Person1.FirstNames,
                //                                                ticket.Couple.Person1.LastName,
                //                                                ticket.Couple.Person2.FirstNames,
                //                                                ticket.Couple.Person2.LastName), "System");
                //}

                SendBookingToMinisterMail(context, ticket);

            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        ///<summary>
        /// Skickar iväg mail att en bokning på annan plats än stadshuset har blivit gjord.
        ///</summary>
        ///<param name="context"></param>
        ///<param name="ministerEmail"></param>
        ///<param name="ticket"></param>
        ///<param name="booking"></param>
        
        public void SendIndividualBookingToMinisterMail(IDataContext context, string ministerEmail, Ticket ticket, Booking booking)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var person1 = string.Format("{0} {1}", ticket.Couple.Person1.FirstNames, ticket.Couple.Person1.LastName);
                var person2 = string.Format("{0} {1}", ticket.Couple.Person2.FirstNames, ticket.Couple.Person2.LastName);

                dbCtx.SendIndividualBookingToMinisterMail(ministerEmail, booking.DateTime.StartDateTime, person1, person2);
              
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        ///<summary>
        /// Skickar iväg mail att en bokning på annan plats än stadshuset har blivit gjord.
        ///</summary>
        ///<param name="context"></param>
        ///<param name="ticket"></param>
        
        public void SendBookingToMinisterMail(IDataContext context, Ticket ticket)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Booking booking = GetBooking(context, ticket.TicketId);
                ITicketAgent ticketAgent = AgentFactory.GetTicketAgent();

                var ministerAgent = AgentFactory.GetMinisterAgent();
                var minister = ministerAgent.GetMinisterByMinisterCode(context, booking.MinisterCode);

                var person1 = string.Format("{0} {1}", ticket.Couple.Person1.FirstNames, ticket.Couple.Person1.LastName);
                var person2 = string.Format("{0} {1}", ticket.Couple.Person2.FirstNames, ticket.Couple.Person2.LastName);          

                if(minister.Email == null)
                {
                    ticketAgent.InsertNotice(context, ticket.TicketId, string.Format(Resources.MinisterHasNoEmail, minister.FullName), "System");
                }
                else
                {
                    dbCtx.SendIndividualBookingToMinisterMail(minister.Email, booking.DateTime.StartDateTime, person1, person2);
                    ticketAgent.InsertHistory(context, ticket.TicketId, 
                        string.Format(Resources.PreliminaryBookingNotificationToMinister, minister.FullName), "System", false);
                }
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }


        ///<summary>
        /// Skickar iväg ett mail, att det finns en ledig tid, till de par som har önskemål
        ///</summary>
        ///<param name="bookingDateTime"></param>
        ///<param name="context"></param>
        
        public void SendPreferredDateNotification(IDataContext context, DateTime bookingDateTime)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                dbCtx.NotifyCouplesWithPreferredDate(bookingDateTime);
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        
        public void SendConfirmedBookingMail(IDataContext context, string recipient, DateTime date)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                dbCtx.SendConfirmedBookingMail(recipient, date);
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }            
        }

        #endregion

        private static Entity.TimeBlock GetTimeBlockEntityByStartDateTime(IDataContext context, DateTime startDateTime)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Entity.TimeBlock timeBlock =
                    dbCtx.TimeBlocks.Where(t => t.StartDateTime == startDateTime).FirstOrDefault();
                return timeBlock;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }
    }

    internal class PrefDates
    {
        public PrefDates(Booking booking)
        {
            if (booking.PreferredDate1.HasValue)
            {
                PrefDate1 = booking.PreferredDate1.Value;

                if(booking.PreferredDate2.HasValue)
                    PrefDate2 = booking.PreferredDate2.Value;
            }
        }

        internal DateTime PrefDate1 { get; set; }
        internal DateTime? PrefDate2 { get; set; }
    }

    /// <summary>
    /// Används för att jämföra datum och tid för två tidblock
    /// </summary>
    internal class MyDateComparer : IEqualityComparer<Entity.TimeBlock>
    {
        #region IEqualityComparer<TimeBlock> Members

        public bool Equals(Entity.TimeBlock x, Entity.TimeBlock y)
        {
            return x.StartDate.Equals(y.StartDate);
        }

        public int GetHashCode(Entity.TimeBlock obj)
        {
            return obj.StartDate.GetHashCode();
        }

        #endregion
    }
}